package mazegame;

import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Random;

import mazegame.Constants.Direction;

public class MazeServerImpl implements MazeServerInterface {

	private int mapSize;
	private int treasureNum;
	private int nextPlayerID;
	private int playerCount;
	private int treasureCount;
	
	private HashMap<Integer, Integer> playerTreasureCounts;
	private HashMap<Integer, MazePosition> playerPositions;
	private HashMap<MazePosition, Integer> treasurePositions;
	private ServerUI serverUI=null;

	public MazeServerImpl() {
		playerTreasureCounts = new HashMap<Integer, Integer>();
		playerPositions = new HashMap<Integer, MazePosition>();
		treasurePositions = new HashMap<MazePosition, Integer>();
	}
	
	public MazeServerImpl(ServerUI serverUi) {
		playerTreasureCounts = new HashMap<Integer, Integer>();
		playerPositions = new HashMap<Integer, MazePosition>();
		treasurePositions = new HashMap<MazePosition, Integer>();
		this.serverUI=serverUi;
	}

		
	@Override
	public synchronized int joinGame() throws RemoteException {
		
		System.out.println("join game called");

		if (serverUI.gameStatus == Constants.ServerStatus.SERVER_STOP
				|| serverUI.gameStatus == Constants.ServerStatus.SERVER_GAME_RUNNING) {
			return -1;
		} else if (serverUI.gameStatus == Constants.ServerStatus.SERVER_IDLE
				|| serverUI.gameStatus == Constants.ServerStatus.SERVER_GAME_STARTING) {
			int newId = nextPlayerID++;
			MazePosition position = new MazePosition(0, 0);
			Random random = new Random();

			// if this the first player, generate treasure positions
			if (newId == 1) {
				for (int i = 0; i < treasureNum; ++i) {
					int x = random.nextInt(mapSize);
					int y = random.nextInt(mapSize);

					if (treasurePositions.containsKey(new MazePosition(x, y))) {
						treasurePositions
								.put(new MazePosition(x, y), treasurePositions
										.get(new MazePosition(x, y)) + 1);
					} else {
						treasurePositions.put(new MazePosition(x, y), 1);
					}
				}
			}

			// generate new position for the player
			do {
				position.x = random.nextInt(mapSize);
				position.y = random.nextInt(mapSize);
			} while (playerPositions.containsKey(position)
					|| treasurePositions.containsKey(position));

			// update variables
			playerCount++;
			playerTreasureCounts.put(newId, 0);
			playerPositions.put(newId, position);

			// if first player, start the timer and update server
			// status
			if (newId == 1) {
				serverUI.startTimer = new GameStartTimer(this.serverUI);
				serverUI.gameStatus = Constants.ServerStatus.SERVER_GAME_STARTING;
				System.out.println("start game timer started");
				System.out.println("game status:"+serverUI.gameStatus);


			}
			System.out.println("new client created with id:"+newId);
			return newId;
		} else {
			return -1;
		}

	}

	@Override
	public synchronized GameState move(int id, Constants.Direction direction)
			throws RemoteException {
		System.out.println("move called with clientid "+id+"  direction " +direction);
		GameState gameState = new GameState();

		// find the location of current player
		MazePosition pos = playerPositions.get(id);

		if (direction!=null && pos != null) {
			// check if the move will move player out of map boundary
			if ((direction == Direction.DIR_UP && pos.x - 1 >= 0)
					|| (direction == Direction.DIR_DOWN && pos.x + 1 < mapSize)
					|| (direction == Direction.DIR_LEFT && pos.y - 1 >= 0)
					|| (direction == Direction.DIR_RIGHT && pos.y + 1 < mapSize)) {
				// check if the move will cause player location conflict
				if ((direction == Direction.DIR_UP && !playerPositions
						.containsValue(new MazePosition(pos.x - 1, pos.y)))
						|| (direction == Direction.DIR_DOWN && !playerPositions
								.containsValue(new MazePosition(pos.x + 1, pos.y)))
						|| (direction == Direction.DIR_LEFT && !playerPositions
								.containsValue(new MazePosition(pos.x, pos.y - 1)))
						|| (direction == Direction.DIR_RIGHT && !playerPositions
								.containsValue(new MazePosition(pos.x, pos.y + 1)))) {
					// now we are sure that this move is legal

					 serverUI.txtStatusView.setText(MazeUtil.log("client move legal",serverUI.txtStatusView.getText())); 
					 
					// generate new player location
					MazePosition newPos = new MazePosition(pos.x, pos.y);
					if (direction == Direction.DIR_UP)
						newPos.x -= 1;
					else if (direction == Direction.DIR_DOWN)
						newPos.x += 1;
					else if (direction == Direction.DIR_LEFT)
						newPos.y -= 1;
					else
						newPos.y += 1;

					// remove old player location and insert new player location
					playerPositions.remove(pos);
					playerPositions.put(id, newPos);
					System.out.println("player new position x "+newPos.x+ " y "+newPos.y);


					// if the new location contains gold, add them to the player
					// and update gold map
					if (treasurePositions.containsKey(newPos)) {
						playerTreasureCounts.put(id,
								playerTreasureCounts.get(id)
										+ treasurePositions.get(newPos));
						treasurePositions.remove(newPos);
					}
				} else {
					serverUI.txtStatusView.setText(MazeUtil.log(
							"client move ilegal",
							serverUI.txtStatusView.getText()));
				}
			} else {
				serverUI.txtStatusView
						.setText(MazeUtil.log("client move ilegal",
								serverUI.txtStatusView.getText()));
			}

		}

		gameState.setPlayerPositions(playerPositions);
		gameState.setPlayerTreasureCounts(playerTreasureCounts);
		gameState.setTreasurePositions(treasurePositions);
		gameState.setGameStartTickcount(serverUI.gameStartTickcount);

		return gameState;

	}

	

	public int getMapSize() throws RemoteException{
		return mapSize;
	}

	public void setMapSize(int mapSize) {
		this.mapSize = mapSize;
	}

	public int getTreasureNum() {
		return treasureNum;
	}

	public void setTreasureNum(int treasureNum) {
		this.treasureNum = treasureNum;
	}

	public int getNextPlayerID() {
		return nextPlayerID;
	}

	public void setNextPlayerID(int nextPlayerID) {
		this.nextPlayerID = nextPlayerID;
	}

	public int getPlayerCount() {
		return playerCount;
	}

	public void setPlayerCount(int playerCount) {
		this.playerCount = playerCount;
	}

	public int getTreasureCount() {
		return treasureCount;
	}

	public void setTreasureCount(int treasureCount) {
		this.treasureCount = treasureCount;
	}

	public HashMap<Integer, Integer> getPlayerTreasureCounts() {
		return playerTreasureCounts;
	}

	public void setPlayerTreasureCounts(
			HashMap<Integer, Integer> playerTreasureCounts) {
		this.playerTreasureCounts = playerTreasureCounts;
	}

	public HashMap<Integer, MazePosition> getPlayerPositions() {
		return playerPositions;
	}

	public void setPlayerPositions(
			HashMap<Integer, MazePosition> playerPositions) {
		this.playerPositions = playerPositions;
	}

	public HashMap<MazePosition, Integer> getTreasurePositions() {
		return treasurePositions;
	}

	public void setTreasurePositions(
			HashMap<MazePosition, Integer> treasurePositions) {
		this.treasurePositions = treasurePositions;
	}

	@Override
	public boolean leaveGame(int id) throws RemoteException {
		// TODO Auto-generated method stub
		return false;
	}

}
